package org.maxgroundstation.groundstation.view.map;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Method;
import java.util.Iterator;

import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicSliderUI;

import org.maxgroundstation.groundstation.GroundStationEvent;
import org.maxgroundstation.groundstation.GroundStationListener;
import org.maxgroundstation.world.WorldModel;

/**
 * A panel for controlling the map view.
 * @author Scott Davis
 * 
 * Copyright (C) 2010  Scott Davis
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
@SuppressWarnings("serial")
public class MapControlPanel extends JPanel {

    // Static members
    private static final String DEFAULT_MAP_TYPE = "Natural Color";
    
    // Data members
    private MapViewPanel viewPanel;
    private JButton displayOptionsButton;
    private JPopupMenu displayOptionsMenu;
    private JSlider zoomSlider;
    private JComboBox mapTypeChoice;
    
    /**
     * Constructor
     * @param viewPanel the map view panel.
     */
    public MapControlPanel(MapViewPanel viewPanel) {
        // Use JPanel constructor.
        super();
        
        // Initialize data members.
        this.viewPanel = viewPanel;
        
        // Set layout.
        setLayout(new BorderLayout());
        
        // Create title panel.
        JPanel titlePanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        add(titlePanel, BorderLayout.NORTH);
        
        // Create title label.
        JLabel titleLabel = new JLabel("GPS Map");
        Font titleFont = titleLabel.getFont().deriveFont(Font.BOLD);
        titleLabel.setFont(titleFont);
        titlePanel.add(titleLabel);
        
        // Create control panel.
        JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 5));
        add(controlPanel, BorderLayout.CENTER);
        
        // Create display options button.
        displayOptionsButton = new JButton("Display Options");
        displayOptionsButton.setToolTipText("Select which options are displayed on the map.");
        displayOptionsButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                if (displayOptionsMenu == null) createDisplayOptionsMenu();
                displayOptionsMenu.show(displayOptionsButton, 0, displayOptionsButton.getHeight());
            }
        });
        controlPanel.add(displayOptionsButton);
        
        // Create a choice for selecting background image style.
        //mapTypeChoice = new JComboBox(world.getMapTypeChoices().split("\t"));
        mapTypeChoice = new JComboBox();
        mapTypeChoice.setToolTipText("Select what the base map should depict.");
        //mapTypeChoice.setSelectedItem(getMapViewPanel().getBaseMapStyle());
        removeComboBoxKeyboardBinding(mapTypeChoice);
        mapTypeChoice.addActionListener(new ActionListener() {
        	public void actionPerformed(ActionEvent event) {
        	    WorldModel world = getMapViewPanel().getGroundStation().getWorldModel();
        	    if (world != null) {
        	        getMapViewPanel().setBaseMapStyle(
        	                world.getMapTypeChoice(mapTypeChoice.getSelectedIndex()));
        	    }
        	}
        } );
        
        viewPanel.getGroundStation().addGroundStationListener(new GroundStationListener() {

            @Override
            public void groundStationUpdate(GroundStationEvent event) {
                if (GroundStationEvent.WORLD_MODEL_SET.equals(event.getType())) {
                    // Populate map types in combo box.
                    WorldModel world = getMapViewPanel().getGroundStation().getWorldModel();
                    String[] mapTypes = world.getMapTypeChoices().split("\t");
                    for (int x = 0; x < mapTypes.length; x++) {
                        mapTypeChoice.addItem(mapTypes[x]);
                    }
                    mapTypeChoice.setSelectedItem(DEFAULT_MAP_TYPE);
                }
            }
        });
        
        controlPanel.add(mapTypeChoice);
        
        
        // Create zoom panel.
        JPanel zoomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
        controlPanel.add(zoomPanel);
        
        // Create zoom label.
        JLabel zoomLabel = new JLabel("Zoom: ");
        zoomPanel.add(zoomLabel);
        
        // Create zoom slider.
        zoomSlider = new JSlider(SwingConstants.HORIZONTAL, -10, 10, 0);
        zoomSlider.setMinorTickSpacing(1);
        zoomSlider.setPaintTicks(true);
        zoomSlider.setPaintTrack(true);
        removeSliderKeyboardBinding(zoomSlider);
        zoomSlider.setToolTipText("Adjust the map zoom.");
        zoomSlider.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                JSlider source = (JSlider) e.getSource();
                int value = source.getValue();
                float zoom = MapViewPanel.DEFAULT_MAP_ZOOM;
                if (value > 0) zoom = zoom / ((float) value + 1F);
                else if (value < 0) zoom = zoom * (Math.abs((float) value) + 1F);
                getMapViewPanel().setZoom(zoom);
            }
        });
        zoomPanel.add(zoomSlider);
        
        // Create the recenter map button.
        JButton recenterButton = new JButton("Recenter Map");
        recenterButton.setToolTipText("Recenter map on the rover.");
        recenterButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                getMapViewPanel().recenterMap();
            }
        });
        controlPanel.add(recenterButton);
    }
    
    /**
     * Get the zoom slider.
     * @return zoom slider.
     */
    public JSlider getZoomSlider() {
        return zoomSlider;
    }
    
    /**
     * Creates the map display options popup menu.
     */
    private void createDisplayOptionsMenu() {
        displayOptionsMenu = new JPopupMenu("Map Display Options");
        
        // Create all map overlay menu items.
        Iterator<String> i = viewPanel.getMapOverlayIDs().iterator();
        while (i.hasNext()) {
            String mapOverlayID = i.next();
            JCheckBoxMenuItem overlayMI = new JCheckBoxMenuItem(mapOverlayID, 
                    viewPanel.isDisplayMapOverlay(mapOverlayID));
            overlayMI.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    JCheckBoxMenuItem item = (JCheckBoxMenuItem) event.getSource();
                    viewPanel.setDisplayMapOverlay(item.getText(), item.isSelected());
                    viewPanel.repaint();
                }
            });
            displayOptionsMenu.add(overlayMI);
        }
    }
    
    /**
     * Gets the map view panel.
     * @return map view panel.
     */
    private MapViewPanel getMapViewPanel() {
        return viewPanel;
    }
    
    /**
     * Removes keyboard bindings from a slider component.
     * @param slider the slider component.
     */
    private void removeSliderKeyboardBinding(JSlider slider) {
        try {
            Method uninstallKeyboardActions = BasicSliderUI.class.getDeclaredMethod(
                    "uninstallKeyboardActions", new Class[] { JSlider.class });
            uninstallKeyboardActions.setAccessible(true);
            uninstallKeyboardActions.invoke(slider.getUI(), slider);
        }
        catch (Exception e) {
            e.printStackTrace(System.err);
        }
    }
    
    /**
     * Removes keyboard bindings from a combo box component.
     * @param cb the combo box component.
     */
    private void removeComboBoxKeyboardBinding(JComboBox cb) {
        try {
            Method uninstallKeyboardActions = BasicComboBoxUI.class.getDeclaredMethod(
                    "uninstallKeyboardActions", new Class[] { });
            uninstallKeyboardActions.setAccessible(true);
            uninstallKeyboardActions.invoke(cb.getUI());
        }
        catch (Exception e) {
            e.printStackTrace(System.err);
        }
    }
}